package model;

import java.util.Random;

public abstract class PhilosopherProtocol {

	private Philosopher philosopher;
	private final Random random;

	public void setPhilosopher(final Philosopher philosopher) {
		this.philosopher = philosopher;
	}

	public Philosopher getPhilosopher() {
		return philosopher;
	}

	public Random getRandom() {
		return random;
	}

	public PhilosopherProtocol() {
		this.random = new Random();
	}

	/**
	 * Waits a random amount below the max wait time of the current {@link PhilosopherState} and changes to the next state.
	 * @throws InterruptedException when an interruption occured during the wait period.
	 */
	public synchronized void waitAndChangeState() throws InterruptedException{
		this.wait((long)(this.getRandom().nextFloat() * this.getPhilosopher().getState().getMaxWaitTime()) + 1);
		this.getPhilosopher().getStateLock().lock();
		final PhilosopherState state = this.getPhilosopher().getState();
		state.accept(new PhilosopherStateVisitor() {
			
			@Override
			public void visit(final Thinking thinking) {
				PhilosopherProtocol.this.onThinkingState();
			}
			
			@Override
			public void visit(final Eating eating) {
				PhilosopherProtocol.this.onEatingState();
			}

			@Override
			public void visit(final LeftTokenObtained leftTokenObtained) {
				PhilosopherProtocol.this.onLeftTokenObtainedState();
			}

			@Override
			public void visit(final StopEating stopEating) {
				PhilosopherProtocol.this.onStopEating();
				if(PhilosopherProtocol.this.getPhilosopher().isStopInitiated()){
					PhilosopherProtocol.this.getPhilosopher().setStopped(true);
				}
			}
		});
		this.getPhilosopher().getStateLock().unlock();
	}

	/**
	 * When the current state is {@link Thinking}.
	 */
	public abstract void onThinkingState();
	/**
	 * When the current state is {@link Eating}.
	 */
	public abstract void onEatingState();
	/**
	 * When the current state is {@link LeftTokenObtained}.
	 */
	public abstract void onLeftTokenObtainedState();
	/**
	 * When the current state is {@link StopEating}.
	 */
	public abstract void onStopEating(); 

}
